#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
package ${package}.setup.module.code.service.impl;

import ${package}.common.bean.po.Code;
import ${package}.common.bean.po.CodeType;
import ${package}.common.bean.vo.AddCodeTypeVo;
import ${package}.common.bean.vo.CodeApiVo;
import ${package}.common.bean.vo.CodeMoveVo;
import ${package}.common.bean.vo.CodeTypeVo;
import ${package}.common.bean.vo.CodeVo;
import ${package}.common.bean.vo.InsertCodeVo;
import ${package}.common.bean.vo.QueryBean;
import ${package}.common.bean.vo.UpdateCodeTypeVo;
import ${package}.common.bean.vo.UpdateCodeVo;
import ${package}.common.bean.vo.UploadCodeVo;
import ${package}.common.emum.CodeCreateWay;
import ${package}.common.emum.CommonStatusEnum;
import ${package}.common.emum.ReturnCodeEnum;
import ${package}.common.exception.BusinessException;
import ${package}.common.utils.ResourceUtils;
import ${package}.common.utils.SnowFlakeUtil;
import ${package}.common.utils.TreeUtil;
import ${package}.setup.module.code.mapper.CodeMapper;
import ${package}.setup.module.code.service.CodeService;
import ${package}.setup.utils.SourceCodeUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import com.github.pagehelper.page.PageMethod;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Description: CodeService
 *
 * @author wenxiaopeng
 * @date 2020/07/24 13:54
 *
 * <pre>
 *              ${copyright}
 *      Copyright (c) 2019. All Rights Reserved.
 * </pre>
 */
@Slf4j
@Service
public class CodeServiceImpl implements CodeService {

	@Resource
	private CodeMapper codeMapper;

	private static final Pattern PATTERN_CODE_STYLE = Pattern.compile("^[a-zA-Z0-9_${symbol_escape}${symbol_escape}-]{1,32}${symbol_dollar}");

	private static final String CODE_FORMAT = "_C%04d";

	// 从第三行开始 填充代码值
	private static final Integer HOLDER_ROWS = 2;

	/**
	 * Description:
	 * <修改代码类型状态>
	 *
	 * @param id   1
	 * @author wenxiaopeng
	 * @date 17:21 2020/07/26
	 **/
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void changeCodeTypeStatus(Long id) {
		CodeType codeType = this.checkCodeTypeExists(id);
		codeType.setStatus(
				codeType.getStatus().equals(CommonStatusEnum.STATUS_VALID.getCode())
						? CommonStatusEnum.STATUS_INVALID.getCode()
						: CommonStatusEnum.STATUS_VALID.getCode()
		);
		codeMapper.updateCodeType(codeType);
	}

	/**
	 * Description:
	 * <单插入一个代码>
	 *
	 * @param codeVo 1
	 * @return ${package}.corplution.bean.po.Code
	 * @author wenxiaopeng
	 * @date
	 **/
	@Override
	@Transactional(rollbackFor = Exception.class)
	public Code insertCode(InsertCodeVo codeVo) {

		Long codeTypeId = codeVo.getCodeTypeId();
		CodeType codeTypePo = this.checkCodeTypeExists(codeTypeId);
		String codeThis = codeVo.getCode();
		if(CodeCreateWay.TBD.getWay().equals(codeTypePo.getCreateWay())) {
			List<Code> codes = codeMapper.findCodeByType(codeTypePo.getId());
			if(codes.isEmpty()) {
				if(StringUtils.isBlank(codeThis)) {
					codeTypePo.setCreateWay(CodeCreateWay.AUTO.getWay());
				} else {
					this.checkInputCodeFormat(codeThis);
					codeTypePo.setCreateWay(CodeCreateWay.MANUAL.getWay());
				}
				codeMapper.updateCodeType(codeTypePo);
			}
		}
		Code code = new Code();
		code.setId(SnowFlakeUtil.getID());
		code.setCodeTypeId(codeTypeId);
		code.setValue(codeVo.getValue());
		code.setCodeOrder(1);
		code.setAka(codeVo.getAka());
		code.setStatus(CommonStatusEnum.STATUS_VALID.getCode());
		Code parent = null;
		// 查其上一个代码，或前一个代码
		if (null != codeVo.getParent()) {
			parent = codeMapper.findCode(codeVo.getParent());
			code.setCodeOrder(parent.getCodeOrder() + 1);
			code.setUpperCodeId(parent.getId());
		}

		if (null != codeVo.getPrevious()) {
			Code previous = codeMapper.findCode(codeVo.getPrevious());
			code.setCodeOrder(previous.getCodeOrder() + 1);
			code.setUpperCodeId(previous.getUpperCodeId());
		}
		synchronized (this) {
			if(CodeCreateWay.MANUAL.getWay().equals(codeTypePo.getCreateWay())) {
				// check 是否赋值
				this.checkInputCodeFormat(codeThis);
			} else {
				// 自动生成code codetype_code
				String latestCode = codeMapper.findLatestCode(codeTypeId);
				int current = 0;
				if(StringUtils.isNotEmpty(latestCode)) {
					String digitPart = latestCode.substring(latestCode.lastIndexOf("_C") + 2);
					current = Integer.parseInt(digitPart);
				}
				codeThis = String.format(CODE_FORMAT, current + 1);
				if (null != parent) {
					codeThis = parent.getCode() + codeThis;
				} else {
					codeThis = codeTypePo.getCodeType() + codeThis;
				}
			}

			code.setCode(codeThis);
			codeMapper.moveCodes(codeTypeId, "back", code.getCodeOrder());
			codeMapper.addCode(code);
		}
		return code;
	}

	/**
	 * Description:
	 * <检查输入的代码是否合规>
	 * @param codeInput 1
	 * @author wenxiaopeng
	 * @date 18:00 2021/06/21
	 **/
	private void checkInputCodeFormat(String codeInput) {
		if (StringUtils.isBlank(codeInput)) {
			throw new BusinessException(ReturnCodeEnum.PARAMETER_NOT_MATCH);
		}
		// 检查代码格式
		if (!PATTERN_CODE_STYLE.matcher(codeInput).matches()) {
			throw new BusinessException("代码格式不符：" + codeInput);
		}
	}

	/**
	 * Description:
	 * <修改代码状态>
	 *
	 * @param id   1
	 * @author wenxiaopeng
	 * @date
	 **/
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void changeCodeStatus(Long id) {
		Code code = codeMapper.findCode(id);
		if(null == code) {
			throw new BusinessException(ReturnCodeEnum.RESOURCE_NOT_FOUND);
		}
		code.setStatus(
				code.getStatus().equals(CommonStatusEnum.STATUS_VALID.getCode())
						? CommonStatusEnum.STATUS_INVALID.getCode()
						: CommonStatusEnum.STATUS_VALID.getCode()
		);
		codeMapper.updateCode(code);
	}

	/**
	 * Description:
	 * <仅适用批量直接插入code，例如导入code excel>
	 *
	 * @param codes       code列表
	 * @return void
	 * @author wenxiaopeng
	 * @date
	 **/
	@Override
	@Transactional(rollbackFor = Exception.class)
	public CodeTypeVo batchInsertCode(List<Code> codes) {
		if(CollectionUtils.isEmpty(codes)) {
			throw new BusinessException(ReturnCodeEnum.CURRENT_ELEMENT_WAS_OCCUPIED);
		}
		codeMapper.batchAddCode(codes);
		return this.getCodeTypeDetail(codes.get(0).getCodeTypeId());
	}

	/**
	 * Description:
	 * <下载所选的代码检索项的所有代码组合>
	 *
	 * @param codeTypeId 1
	 * @return org.apache.poi.xssf.usermodel.XSSFWorkbook
	 * @author zhoutt
	 * @date 21:10 2020/9/1
	 **/
	@Override
	public XSSFWorkbook downloadCode(Long codeTypeId) {

		CodeType codeType = this.checkCodeTypeExists(codeTypeId);
		List<Code> codes = codeMapper.findCodeByType(codeTypeId);
		if(codes.isEmpty()) {
			throw new BusinessException(ReturnCodeEnum.RESOURCE_NOT_FOUND);
		}
		// 获取模板
		XSSFWorkbook workbook;
		try(InputStream resourceAsStream = ResourceUtils.getResourceAsStream("template/code_import.xlsx")) {
			workbook = new XSSFWorkbook(resourceAsStream);
		} catch (IOException e) {
			workbook = new XSSFWorkbook();
			this.initSheet0(workbook, codeType.getDescription());
		}

		XSSFSheet sheet = workbook.getSheetAt(0);
		workbook.setMissingCellPolicy(Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
		// 从第三行开始 填充代码值
		Map<Long, String> idCodeMap = codes.stream().collect(Collectors.toMap(Code::getId, Code::getCode, (o, n) -> n));
		for(int rowNo = HOLDER_ROWS; rowNo<codes.size() + HOLDER_ROWS; rowNo++) {
			XSSFRow row = sheet.getRow(rowNo);
			if(null==row) {
				row = sheet.createRow(rowNo);
			}
			Code code = codes.get(rowNo - HOLDER_ROWS);
			if(null != code.getUpperCodeId()) {
				row.getCell(0).setCellValue(idCodeMap.getOrDefault(code.getUpperCodeId(), StringUtils.EMPTY));
			}
			row.getCell(1).setCellValue(code.getCode());
			row.getCell(2).setCellValue(code.getValue());
			row.getCell(3).setCellValue(code.getAka());
		}
		return workbook;
	}

	/**
	 * Description:
	 * <获取codeType详情>
	 *
	 * @param codeTypeId 1
	 * @return ${package}.common.bean.vo.CodeTypeVo
	 * @author wenxiaopeng
	 * @date 14:46 2021/06/21
	 **/
	@Override
	public CodeTypeVo getCodeTypeDetail(Long codeTypeId) {
		CodeType codeType = codeMapper.findCodeTypeById(codeTypeId);
		CodeTypeVo codeTypeVo = codeType.asVo();
		List<Code> codes = codeMapper.findCodeByType(codeTypeId);
		if(!codes.isEmpty()) {
			List<CodeVo> vos = codes.stream().map(Code::asVo).collect(Collectors.toList());
			codeTypeVo.setCodes(TreeUtil.fillingTree(vos));
		}
		return codeTypeVo;
	}

	/**
	 * Description:
	 * <批量上传code>
	 *
	 * @param codeVo 1
	 * @return ${package}.common.bean.vo.CodeTypeVo
	 * @author wenxiaopeng
	 * @date 14:56 2021/06/21
	 **/
	@Override
	@Transactional(rollbackFor = Exception.class)
	public CodeTypeVo handleImport(UploadCodeVo codeVo) {
		// 获取文件名
		@NotNull MultipartFile codeFile = codeVo.getCodeFile();
		CodeType codeType = this.checkCodeTypeExists(codeVo.getCodeTypeId());
		if(CodeCreateWay.AUTO.getWay().equals(codeType.getCreateWay())) {
			// 不允许混动
			throw new BusinessException(ReturnCodeEnum.CURRENT_ELEMENT_WAS_OCCUPIED);
		}
		try(XSSFWorkbook wb = new XSSFWorkbook(codeFile.getInputStream())) {
			int numberOfSheets = wb.getNumberOfSheets();
			wb.setMissingCellPolicy(Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
			Assert.isTrue((numberOfSheets > 0), "Empty Excel");
			// 处理代码导入
			// 是否
			// 获取首页
			XSSFSheet sheet = wb.getSheetAt(0);
			XSSFRow firstRow = sheet.getRow(HOLDER_ROWS);
			Cell cell = firstRow.getCell(1);
			// 是否自动生成代码
			boolean autoCode = false;
			if(null==cell || StringUtils.isBlank(cell.getStringCellValue())) {
				autoCode = true;
			}
			Iterator<Row> rowIterator = sheet.iterator();
			int order = 1;
			List<Code> codes = Lists.newArrayListWithExpectedSize(sheet.getPhysicalNumberOfRows());
			// 用于id定位
			Map<String, Long> locator = Maps.newHashMapWithExpectedSize(codes.size());
			// 存储上级代码映射
			Map<Integer, String> upperCodeMapper = Maps.newHashMapWithExpectedSize(codes.size());
			while (rowIterator.hasNext()) {
				Row next = rowIterator.next();
				if(next.getRowNum() >= HOLDER_ROWS) {
					String code = next.getCell(1).getStringCellValue();
					if(!autoCode) {
						this.checkInputCodeFormat(code);
					}
					Code po = new Code(SnowFlakeUtil.getID(), code, next.getCell(2).getStringCellValue(), next.getCell(3).getStringCellValue()
							, null, codeType.getId(), order, CommonStatusEnum.STATUS_VALID.getCode());
					if(autoCode) {
						po.setCode(null);
						locator.put(String.valueOf(next.getRowNum() + 1), po.getId());
					} else {
						locator.put(code, po.getId());
					}
					codes.add(po);
					Cell upperLocator = next.getCell(0);
					String upperCode;
					if(CellType.NUMERIC.equals(upperLocator.getCellType())) {
						upperCode = String.valueOf((int)upperLocator.getNumericCellValue());
					} else {
						upperCode = upperLocator.toString();
					}
					if(StringUtils.isNotBlank(upperCode)) {
						upperCodeMapper.put(order - 1, upperCode);
					}
					order++;
				}
			}
			if (codes.isEmpty()) {
				throw new BusinessException(ReturnCodeEnum.PARAMETER_NOT_MATCH);
			}
			this.setUpperCodeId(codes, locator, upperCodeMapper);
			if(autoCode) {
				codeType.setCreateWay(CodeCreateWay.AUTO.getWay());
			} else {
				codeType.setCreateWay(CodeCreateWay.MANUAL.getWay());
			}
			this.batchGenerateCode(codeType.getCodeType(), codes);
			CODE_ORDER_HOLDER.remove();
			codeMapper.updateCodeType(codeType);
			return this.batchInsertCode(codes);
		} catch (IOException e) {
			log.error("error info",e);
			throw new BusinessException(e.getMessage());
		}
	}

	/**
	 * Description:
	 * <>
	 *
	 * @param code 1
	 * @return ${package}.common.bean.vo.CodeVo
	 * @author wenxiaopeng
	 * @date 09:37 2021/06/22
	 **/
	@Override
	public CodeApiVo getCodeDetail(String code) {
		Code codePo = codeMapper.findCodeVoByCode(code);
		return codePo.asApiVo();
	}

	/** 保存批量导入的代码顺序 */
	private static final ThreadLocal<AtomicInteger> CODE_ORDER_HOLDER = ThreadLocal.withInitial(() -> new AtomicInteger(1));

	/**
	 * Description:
	 * <获取代码类型列表>
	 *
	 * @return java.util.List<${package}.common.bean.vo.CodeTypeVo>
	 * @author wenxiaopeng
	 * @date 10:07 2021/06/22
	 **/
	@Override
	public List<CodeType> getCodeTypeList() {
		return codeMapper.findCodeTypeList();
	}

	/**
	 * Description:
	 * <通过id获取代码详情>
	 *
	 * @param id 1
	 * @return ${package}.common.bean.vo.CodeVo
	 * @author wenxiaopeng
	 * @date 09:46 2021/06/22
	 **/
	@Override
	public CodeVo getCodeDetail(Long id) {
		Code code = codeMapper.findCode(id);
		if(null != code) {
			return code.asVo();
		}
		return null;
	}

	/**
	 * Description:
	 * <>
	 * @param codeType  代码类型
	 * @param codes     代码列表
	 * @author wenxiaopeng
	 * @date 16:33 2021/06/21
	 **/
	private void batchGenerateCode(String codeType, List<Code> codes) {
		// upper -> codes
		List<Code> level1Codes = codes.stream().filter(item -> null==item.getUpperCodeId()).collect(Collectors.toList());
		Map<Long, List<Code>> subCodesMap = codes.stream().filter(item -> null!=item.getUpperCodeId()).collect(Collectors.groupingBy(Code::getUpperCodeId));
		int index = 1;
		for (Code top : level1Codes) {
			if(StringUtils.isBlank(top.getCode())) {
				top.setCode(String.format(codeType + CODE_FORMAT, index++));
			}
			top.setCodeOrder(CODE_ORDER_HOLDER.get().getAndIncrement());
			this.recurseSet(top, subCodesMap);
		}
	}

	/**
	 * Description:
	 * <递归设置code>
	 * @param upper 1
	 * @param subCodesMap 2
	 * @author wenxiaopeng
	 * @date 17:04 2021/06/21
	 **/
	private void recurseSet(Code upper, Map<Long, List<Code>> subCodesMap) {
		List<Code> subCodes = subCodesMap.get(upper.getId());
		if(CollectionUtils.isNotEmpty(subCodes)) {
			int index = 1;
			for (Code code : subCodes) {
				if(StringUtils.isBlank(code.getCode())) {
					code.setCode(String.format(upper.getCode() + CODE_FORMAT, index++));
				}
				code.setCodeOrder(CODE_ORDER_HOLDER.get().getAndIncrement());
				if (CollectionUtils.isNotEmpty(subCodesMap.get(code.getId()))) {
					this.recurseSet(code, subCodesMap);
				}
			}
		}
	}

	/**
	 * Description:
	 * <填充code的信息，包括上级code以及code值（若是自动生成）>
	 * @param codes             代码列表
	 * @param locator           定位代码id
	 * @param upperCodeMapper   存储原始的上级代码信息
	 * @author wenxiaopeng
	 * @date 16:17 2021/06/21
	 **/
	private void setUpperCodeId(List<Code> codes, Map<String, Long> locator, Map<Integer, String> upperCodeMapper) {
		// 填充上级代码信息
		for (int i = 0; i < codes.size(); i++) {
			Code code = codes.get(i);
			String upperCode = upperCodeMapper.get(i);
			if (StringUtils.isNotBlank(upperCode)) {
				code.setUpperCodeId(locator.get(upperCode));
			}
		}
	}

	private CodeType checkCodeTypeExists(Long codeTypeId) {
		// 获取codeType
		CodeType codeType = codeMapper.findCodeTypeById(codeTypeId);
		if(null==codeType) {
			throw new BusinessException(ReturnCodeEnum.RESOURCE_NOT_FOUND);
		}
		return codeType;
	}

	private boolean emptyRow(XSSFRow currRow) {
		int columnNum = currRow.getPhysicalNumberOfCells();
		for (int i = 0; i < columnNum; i++) {
			XSSFCell cell = currRow.getCell(i);
			if (null != cell && StringUtils.isNotBlank(cell.getStringCellValue())) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Description:
	 * <生成代码文件>
	 *
	 * @param codeTypeId 1
	 * @param style      2
	 * @return java.io.File
	 * @author wenxiaopeng
	 * @date 13:02 2021/06/21
	 **/
	@Override
	public File generateSourceCode(Long codeTypeId, SourceCodeUtil.CodeStyle style) {
		CodeType codeType = codeMapper.findCodeTypeById(codeTypeId);
		if(null == codeType) {
			throw new BusinessException(ReturnCodeEnum.RESOURCE_NOT_FOUND);
		}
		List<Code> codes = codeMapper.findCodeByType(codeTypeId);
		return SourceCodeUtil.getSourceCode(codeType, codes, style);
	}

	private void initSheet0(XSSFWorkbook workbook, String description) {
		XSSFSheet sheet = workbook.getSheetAt(0);
		// 前两行表头，第一行是col_0001,第二行是字段别名
		XSSFRow row1 = sheet.createRow(0);
		XSSFRow row2 = sheet.createRow(1);
		row1.getCell(0).setCellValue(description);
		CellRangeAddress rangeAddress = new CellRangeAddress(0, 0, 0, 3);
		sheet.addMergedRegion(rangeAddress);
		row2.getCell(0).setCellValue("上级代码");
		row2.getCell(1).setCellValue("代码");
		row2.getCell(2).setCellValue("代码含义");
		row2.getCell(3).setCellValue("业务代码");
	}

	/**
	 * Description:
	 * <修改修订版本代码值>
	 *
	 * @param codeVo 1
	 * @return ${package}.corplution.bean.po.Code
	 * @author wenxiaopeng
	 * @date 10:38 2020/09/07
	 **/
	@Override
	@Transactional(rollbackFor = Exception.class)
	public Code updateCode(UpdateCodeVo codeVo) {
		Code code = codeMapper.findCode(codeVo.getId());
		if(null == code) {
			throw new BusinessException(ReturnCodeEnum.RESOURCE_NOT_FOUND);
		}
		code.setValue(codeVo.getValue());
		code.setAka(codeVo.getAka());
		codeMapper.updateCode(code);
		return code;
	}

	/**
	 * Description:
	 * <更新代码类型描述>
	 *
	 * @param codeVo 1
	 * @return ${package}.corplution.bean.po.CodeType
	 * @author wenxiaopeng
	 * @date 10:49 2020/09/07
	 **/
	@Override
	@Transactional(rollbackFor = Exception.class)
	public CodeType updateCodeType(UpdateCodeTypeVo codeVo) {
		CodeType codeType = codeMapper.findCodeTypeById(codeVo.getId());
		if(null == codeType) {
			throw new BusinessException(ReturnCodeEnum.RESOURCE_NOT_FOUND);
		}
		codeType.setDescription(codeVo.getDescription());
		codeMapper.updateCodeType(codeType);
		return codeType;
	}

	/**
	 * Description:
	 * <代码值维护可拖动排序>
	 * @param codeMoveVo 1
	 * @author wenxiaopeng
	 * @date 09:08 2021/06/21
	 **/
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void moveCode(CodeMoveVo codeMoveVo) {
		Code po = codeMapper.findCode(codeMoveVo.getId());
		Code target = codeMapper.findCode(codeMoveVo.getTarget());
		if(Objects.isNull(po) || Objects.isNull(target)) {
			throw new BusinessException(ReturnCodeEnum.RESOURCE_NOT_FOUND);
		}
		// 移动两个代码中间的代码
		if (po.getCodeOrder() < target.getCodeOrder()) {
			codeMapper.moveCodesForward(po.getCodeTypeId(), po.getCodeOrder(), target.getCodeOrder());
		} else {
			codeMapper.moveCodesBackward(po.getCodeTypeId(),  target.getCodeOrder(), po.getCodeOrder());
		}
		// 修改目标代码顺序
		po.setCodeOrder(target.getCodeOrder());
		codeMapper.updateCode(po);
	}

	/**
	 * Description:
	 * <新增代码类型>
	 *
	 * @param codeType 1
	 * @return ${package}.common.bean.po.CodeType
	 * @author wenxiaopeng
	 * @date 14:20 2021/06/17
	 **/
	@Override
	@Transactional(rollbackFor = Exception.class)
	public CodeType addCodeType(AddCodeTypeVo codeType) {
		CodeType newType = new CodeType(SnowFlakeUtil.getID(), codeType.getCodeType(), CodeCreateWay.TBD.getWay(), codeType.getDescription(), CommonStatusEnum.STATUS_VALID.getCode());
		try {
			codeMapper.addCodeType(newType);
		} catch (Exception e) {
			throw new BusinessException(ReturnCodeEnum.CURRENT_ELEMENT_WAS_OCCUPIED);
		}
		return newType;
	}

	/**
	 * Description:
	 * <分页获取codeType列表>
	 *
	 * @param queryBean 查询条件
	 * @return com.github.pagehelper.PageInfo<${package}.common.bean.po.CodeType>
	 * @author wenxiaopeng
	 * @date 14:27 2021/06/17
	 **/
	@Override
	public PageInfo<CodeType> getCodeTypeLimits(QueryBean queryBean) {
		Page<CodeType> page = PageMethod.startPage(queryBean.getCurrent(), queryBean.getPageSize());
		codeMapper.findCodeTypeLimits(queryBean);
		return new PageInfo<>(page);
	}

	/**
	 * Description:
	 * <查询指定代码列表>
	 * @param codeType 1
	 * @param code 2
	 * @param lazy 3
	 * @author wenxiaopeng
	 * @date 09:29 2021/06/21
	 * @return java.util.List<${package}.common.bean.vo.CodeVo>
	 **/
	@Override
	public List<CodeApiVo> getCodes(String codeType, String code, boolean lazy) {
		// 转为id查询
		CodeType po = codeMapper.findCodeTypeByCode(codeType);
		if(Objects.isNull(po)) {
			throw new BusinessException(ReturnCodeEnum.RESOURCE_NOT_FOUND);
		}
		CodeTypeVo codeTypeVo = po.asVo();

		Code targetCode;
		// 首先加载当前code及其第一级子Code
		Long upperCodeId = null;
		if(StringUtils.isNotBlank(code)) {
			targetCode = codeMapper.findCodeByTypeAndCode(codeTypeVo.getId(), code);
			if(!Objects.isNull(targetCode)) {
				upperCodeId = targetCode.getId();
			}
		}
		List<Code> codes = codeMapper.findCodeByTypeAndUpperCode(codeTypeVo.getId(), upperCodeId);

		if(lazy) {
			return codes.stream().map(Code::asApiVo).collect(Collectors.toList());
		} else {
			// 递归获取全部子代码
			if(codes.isEmpty()) {
				return Collections.emptyList();
			}
			List<CodeApiVo> result = Lists.newArrayListWithCapacity(64);
			while (CollectionUtils.isNotEmpty(codes)) {
				result.addAll(codes.stream().map(Code::asApiVo).collect(Collectors.toList()));
				Set<Long> upperCodeSet = codes.stream().map(Code::getId).collect(Collectors.toSet());
				codes = codeMapper.findSubCodes(upperCodeSet);
			}

			return result;
		}
	}

}
